package net.cyue.simple.container.injector;

import net.cyue.util.ReflectUtil;
import net.cyue.util.StringUtil;
import net.cyue.simple.container.annotation.Inject;
import net.cyue.simple.container.annotation.Injectable;
import net.cyue.simple.container.data.InjectableMetaType;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class Injector {

    private final AbstractInstanceRegistry instanceRegistry;

    public Injector(AbstractInstanceRegistry registry) {
        this.instanceRegistry = registry;
    }

    /**
     * @param inject 注解
     * @return name 或者 null
     */
    public static String getInjectName(Inject inject) {
        if (inject == null) {
            throw new NullPointerException("inject is null");
        }
        String name = inject.value();
        if (StringUtil.isBlank(name)) {
            name = inject.name();
        }
        if (StringUtil.isBlank(name)) {
            return null;
        }
        return name;
    }

    /**
     * @param injectable 注解
     * @return name 或者 null
     */
    public static String getInjectableName(Injectable injectable) {
        if (injectable == null) {
            throw new NullPointerException("injectable is null");
        }
        String name = injectable.value();
        if (StringUtil.isBlank(name)) {
            name = injectable.name();
        }
        if (StringUtil.isBlank(name)) {
            return null;
        }
        return name;
    }

    public void doInject(Object obj, Field field) throws InvocationTargetException {
        try {
            if (field.get(obj) != null) {
                return;
            }
        } catch (IllegalAccessException e) {
            return;
        }
        Class<?> clz = field.getType();
        Inject inject = field.getAnnotation(Inject.class);
        if (inject == null) {
            Object instance = this.instanceRegistry.getInstance(clz);
            ReflectUtil.setFieldValue(obj, field, instance);
            return;
        }
        String name = Injector.getInjectName(inject);
        if (name == null) {
            Object instance = this.instanceRegistry.getInstance(clz);
            ReflectUtil.setFieldValue(obj, field, instance);
            return;
        }
        InjectableMetaType iMT = new InjectableMetaType(clz, name);
        Object instance = this.instanceRegistry.getInstance(iMT);
        ReflectUtil.setFieldValue(obj, field, instance);
    }
    public void doInject(Object obj) {
        if (obj == null) {
            throw new NullPointerException();
        }
        Field[] fields = obj.getClass().getDeclaredFields();
        for(Field f : fields) {
            try {
                if (f.get(obj) == null) {
                    f.setAccessible(true);
                    this.doInject(obj, f);
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
